package com.antfin.exam.bonus.service;

import com.antfin.exam.bonus.dao.BonusScoreDao;
import com.antfin.exam.bonus.event.ReduceScoreEvent;
import com.antfin.exam.bonus.exception.NotEnoughException;
import com.antfin.exam.bonus.model.ReduceScoreEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.UUID;

@Service
public class BonusService {
    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    protected DistributedLockService  lockService;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private BonusScoreDao bonusScoreDao;



    /**
     * 用户领抢指定的积分池下的积分
     * @param userId 用户号
     * @param bonusScoreId 积分池ID
     * @param score 用户请求的分值
     * @return
     */
    public int takeBonusScore(String userId ,String bonusScoreId,int score){
        if(score<=0){
            throw new IllegalArgumentException("参数score异常，领抢积分不能小于0");
        }

        int reduceScore = 0;
        String lockValue =  UUID.randomUUID().toString().replace("-", "").toUpperCase();

        //1.读取redis的分布式锁（获取或等待锁）
        lockService.lock(bonusScoreId,lockValue);

        //2.读取剩余积分值，并且计算出用户抢到的分值
        int remainScore = getRemainScore(bonusScoreId);
        if(remainScore == 0){
            throw new NotEnoughException("库存剩余积分已被抢光");
        }
        reduceScore = Math.min(score,remainScore);    //把剩余分数和要求分数作比对，如果超了，则把剩余的都给他
        remainScore -= reduceScore;

        //3.把剩余分值写回去，写回去之后，立马释放锁
        setRemainScore(bonusScoreId,remainScore);
        lockService.unlock(bonusScoreId,lockValue);

        //5.把扣减情况记录到redis,并且推送扣减日志
        String logKey = String.format("log:%s-%s",userId,bonusScoreId);
        redisTemplate.opsForValue().set(logKey,reduceScore);

        pushReduceLog(userId,bonusScoreId,reduceScore,remainScore);

        return reduceScore;
    }

    /**
     * 取目前和剩余分数，直接从缓存中读取，如果缓存也没有，则从数据库中读取，读取后，放入缓存中
     * @param bonusScoreId
     * @return
     */
    private int getRemainScore(String bonusScoreId){
        String key = String.format("bouns.%s",bonusScoreId);
        //先到redis中去查
        Object value = redisTemplate.opsForValue().get(key);
        int score = 0;
        if(value != null){
            if(value instanceof Integer){
                score = (Integer)value;
            }else{
                score = Integer.valueOf(String.valueOf(value));
            }
        }else{
            score = bonusScoreDao.queryRemainScore(bonusScoreId);
            redisTemplate.opsForValue().set(key,score);     //写入缓存
        }
        return score;
    }

    /**
     * 更新目前的剩余分数
     * @param bonusScoreId
     * @param remainScore
     */
    private void setRemainScore(String bonusScoreId,int remainScore){
        String key = String.format("bouns.%s",bonusScoreId);
        redisTemplate.opsForValue().set(key,remainScore);     //写入缓存
    }

    /**
     * 推送扣减事件，
     * @param userId
     * @param bonusScoreId
     * @param reduceScore
     * @param remainScore
     */
    private void pushReduceLog(String userId,String bonusScoreId,int reduceScore,int remainScore){
        ReduceScoreEntity entity = new ReduceScoreEntity();
        entity.setBonusScoreId(bonusScoreId);
        entity.setUserId(userId);
        entity.setReduceScore(reduceScore);
        entity.setRemainScore(remainScore);

        applicationContext.publishEvent(new ReduceScoreEvent(this,entity));
    }
}
